#include <io.h>
#include <stdio.h>
#include <string.h>
#include "ipv4.h"
#include "udp.h"
#include "../utility.h"


static alt_u32 		rx_sipv4;			//!< IP отправителя
static alt_u8		rx_protocol;		//!< Тип протокола

static alt_u32 		tx_sipv4;			//!< IP отправителя
static alt_u32		tx_dipv4;			//!< IP получателя
static alt_u8		tx_protocol;		//!< Тип протокола

static alt_u8		payload_ofst;
static alt_u16		payload_size;	//!< Размер полезных данных
static alt_u16		identification;	//!< Идентификатор пакета

// Метод задающий ip источника
alt_u8 ipv4_set_source_ip(alt_u32 ip)
{
	tx_sipv4	=	hton32(ip);

	return 0;
}

// Метод задающий ip назначения
alt_u8 ipv4_set_destination_ip(alt_u32 ip)
{
	tx_dipv4	=	hton32(ip);
	return 0;
}

alt_u8 ipv4_set_protocol(alt_u16 prot)
{
	tx_protocol = prot;
	return 0;
}


// Рассчёт контрольной суммы для IP (и других протоколов)
alt_u16 ipv4_checksum(alt_u32 sum, alt_u8 *buf, alt_u16 len)
{
    // Рассчитываем сумму word'ов блока (big endian)
    // (блок выравнивается на word нулём)
    while(len >= 2)
    {
        sum += ((alt_u16)*buf << 8) | *(buf+1);
        buf += 2;
        len -= 2;
    }

    if(len)
        sum += (alt_u16)*buf << 8;

    // Складываем старший и младший word суммы
    // пока не получим число, влезающее в word
    while(sum >> 16)
        sum = (sum & 0xffff) + (sum >> 16);

    // Снова конвертируем в big endian и берём дополнение
    return ~hton16((alt_u16)sum);
}

// Обработчик пакетов IP
alt_u8 ipv4_header_parser(alt_u8* header)
{
	alt_u8 			err = 0;		// Код ошибки
	ipv4_header_t*	ipv4_header;	// Указатель на заголовок ipv4
	alt_u8 			ver;

	ipv4_header = (ipv4_header_t*)(header);

	ver = ipv4_header->version;

	if (ver == 4)
	{
		rx_sipv4	= ipv4_header->s_ip;
		rx_protocol = ipv4_header->protocol;
		switch (rx_protocol)
		{
		// Если это был Пакет UDP.
		case UDP :
			err = udp_header_parser(header + IPV4_HEAD_SIZE);
			if (err == 0)
			{
				payload_ofst = IPV4_HEAD_SIZE + UDP_HEAD_SIZE;
				payload_size = ntoh16(ipv4_header->length) - IPV4_HEAD_SIZE - UDP_HEAD_SIZE;
			}
			break;

		default	:
			break;
		}
	}
	else
	{
		err = 1;
	}

	return err;
}

// Метод формирующий заголовок, по заданному адресу
alt_u8  ipv4_create_head(alt_u8* header, alt_u16 data_size)
{
	ipv4_header_t ipv4_header;


	identification++;
	ipv4_header.ihl			= IPV4_HEAD_SIZE / 4;
	ipv4_header.version		= IPV4_VERSION;
	ipv4_header.tos			= 0;
	ipv4_header.length			= hton16(data_size + IPV4_HEAD_SIZE);
	ipv4_header.identification	= hton16(identification);
	ipv4_header.flags			= 0;
	ipv4_header.offset			= 0;
	ipv4_header.ttl				= 128;
	ipv4_header.protocol		= tx_protocol;
	ipv4_header.head_checksumm	= 0;
	ipv4_header.s_ip			= tx_sipv4;
	ipv4_header.d_ip			= tx_dipv4;

	ipv4_header.head_checksumm	= ipv4_checksum(0, (void*)&ipv4_header, sizeof(ipv4_header_t));
	memcpy((void*)header, (void*)&ipv4_header, sizeof(ipv4_header_t));

	return 0;
}

// Получить смещение на поле данных и размер поля
alt_u8 ipv4_get_payload(alt_u32* ofst, alt_u16* size)
{
	*ofst 	= payload_ofst;
	*size	= payload_size;

	return 0;
}


alt_u16 ipv4_get_protocol()
{
	return rx_protocol;
}

// Метод задающий ip назначения
alt_u32 ipv4_get_source_ip()
{
	return ntoh32(rx_sipv4);
}
